﻿using System;
using System.Collections.Generic;

using DigitalQuran.Extensions;

namespace DigitalQuran.Numerology {
    /// <summary>
    /// Privide methods for Polygon (Closed figure of n-sides)
    /// </summary>
    public static class Polygon {
        /// <summary>
        /// Limit of Series
        /// </summary>
        private const int SeriesLimit = 100;

        #region Polygon Numbers

        /* http://en.wikipedia.org/wiki/Polygonal_number
         * ----------------------------------------------------------------------------------------
         * N   Name            Formula            i = 1 2 3 4 5 6 7 8 9 10             OEIS number
         * ----------------------------------------------------------------------------------------
         * 3   Triangular      ½(1n² + 1n)         1 3 6 10 15 21 28 36 45 55           A000217 
         * 4   Square          ½(2n² - 0n)         1 4 9 16 25 36 49 64 81 100          A000290 
         * 5   Pentagonal      ½(3n² - 1n)         1 5 12 22 35 51 70 92 117 145        A000326 
         * 6   Hexagonal       ½(4n² - 2n)         1 6 15 28 45 66 91 120 153 190       A000384 
         * 7   Heptagonal      ½(5n² - 3n)         1 7 18 34 55 81 112 148 189 235      A000566 
         * 8   Octagonal       ½(6n² - 4n)         1 8 21 40 65 96 133 176 225 280      A000567 
         * 9   Nonagonal       ½(7n² - 5n)         1 9 24 46 75 111 154 204 261 325     A001106 
         * 10  Decagonal       ½(8n² - 6n)         1 10 27 52 85 126 175 232 297 370    A001107 
         * 11  Hendecagonal    ½(9n² - 7n)         1 11 30 58 95 141 196 260 333 415    A051682 
         * 12  Dodecagonal     ½(10n² - 8n)        1 12 33 64 105 156 217 288 369 460   A051624 
         * 13  Tridecagonal    ½(11n² - 9n)        1 13 36 70 115 171 238 316 405 505   A051865 
         * 14  Tetradecagonal  ½(12n² - 10n)       1 14 39 76 125 186 259 344 441 550   A051866 
         * 15  Pentadecagonal  ½(13n² - 11n)       1 15 42 82 135 201 280 372 477 595   A051867 
         * 16  Hexadecagonal   ½(14n² - 12n)       1 16 45 88 145 216 301 400 513 640   A051868 
         * 17  Heptadecagonal  ½(15n² - 13n)       1 17 48 94 155 231 322 428 549 685   A051869 
         * 18  Octadecagonal   ½(16n² - 14n)       1 18 51 100 165 246 343 456 585 730  A051870 
         * 19  Nonadecagonal   ½(17n² - 15n)       1 19 54 106 175 261 364 484 621 775  A051871 
         * 20  Icosagonal      ½(18n² - 16n)       1 20 57 112 185 276 385 512 657 820  A051872 
         * 21  Icosihenagonal  ½(19n² - 17n)       1 21 60 118 195 291 406 540 693 865  A051873 
         * 22  Icosidigonal    ½(20n² - 18n)       1 22 63 124 205 306 427 568 729 910  A051874 
         * 23  Icositrigonal   ½(21n² - 19n)       1 23 66 130 215 321 448 596 765 955  A051875 
         * 24  Icositetragonal ½(22n² - 20n)       1 24 69 136 225 336 469 624 801 1000 A051876
         * ----------------------------------------------------------------------------------------
         */
        static Dictionary<uint, List<uint>> polygonNumbersDictionary = new Dictionary<uint, List<uint>>();
        /// <summary>
        /// Get Polygon number for specified sided polygon
        /// </summary>
        /// <param name="sides">Sides of polygon</param>
        /// <returns>Returns the Series of Polygon Numbers</returns>
        public static List<uint> PolygonNumbers(uint sides) {
            if (!polygonNumbersDictionary.ContainsKey(sides))
                GeneratePolygonNumbers(sides);

            if (polygonNumbersDictionary.ContainsKey(sides))
                return polygonNumbersDictionary[sides];

            return null;
        }

        static void GeneratePolygonNumbers(uint sides) {
            List<uint> polygon_numbers = new List<uint>(SeriesLimit);
            for (uint n = 1; n <= SeriesLimit; n++) {
                uint number = ((sides - 2) * n * n - (sides - 4) * n) / 2;
                polygon_numbers.Add(number);
            }
            polygonNumbersDictionary.Add(sides, polygon_numbers);
        }

        /// <summary>
        /// Check whether specific number is in polygon number series
        /// </summary>
        /// <param name="sides">Polygon sides</param>
        /// <param name="number">Number to check</param>
        /// <returns>Returns true if found</returns>
        public static bool IsPolygonNumber(uint sides, uint number) {
            if (polygonNumbersDictionary.ContainsKey(sides))
                return (polygonNumbersDictionary[sides].Contains(number));
            else
                return false;
        }

        /// <summary>
        /// Gets Polygon Number for triangulars
        /// </summary>
        public static List<uint> Triangulars { get { return PolygonNumbers(3); } }

        /// <summary>
        /// Gets Polygon Number for Squares
        /// </summary>
        public static List<uint> Squares { get { return PolygonNumbers(4); } }

        /// <summary>
        /// Gets Polygon Number for Pentagonals
        /// </summary>
        public static List<uint> Pentagonals { get { return PolygonNumbers(5); } }

        /// <summary>
        /// Gets Polygon Number for Hexagonals
        /// </summary>
        public static List<uint> Hexagonals { get { return PolygonNumbers(6); } }

        /// <summary>
        /// Gets Polygon Number for Heptagonals
        /// </summary>
        public static List<uint> Heptagonals { get { return PolygonNumbers(7); } }

        /// <summary>
        /// Gets Polygon Number for Octagonals
        /// </summary>
        public static List<uint> Octagonals { get { return PolygonNumbers(8); } }

        /// <summary>
        /// Gets Polygon Number for Nonagonals
        /// </summary>
        public static List<uint> Nonagonals { get { return PolygonNumbers(9); } }

        /// <summary>
        /// Gets Polygon Number for Decagonals
        /// </summary>
        public static List<uint> Decagonals { get { return PolygonNumbers(10); } }

        /// <summary>
        /// Gets Polygon Number for  Hendecagonals
        /// </summary>
        public static List<uint> Hendecagonals { get { return PolygonNumbers(11); } }

        /// <summary>
        /// Gets Polygon Number for Dodecagonals
        /// </summary>
        public static List<uint> Dodecagonals { get { return PolygonNumbers(12); } }

        /// <summary>
        /// Gets Polygon Number for Tridecagonals
        /// </summary>
        public static List<uint> Tridecagonals { get { return PolygonNumbers(13); } }

        /// <summary>
        /// Gets Polygon Number for tetadecagonals
        /// </summary>
        public static List<uint> Tetradecagonals { get { return PolygonNumbers(14); } }

        /// <summary>
        /// Gets Polygon Number for Pentadecagonals
        /// </summary>
        public static List<uint> Pentadecagonals { get { return PolygonNumbers(15); } }

        /// <summary>
        /// Gets Polygon Number for Hexadecagonals
        /// </summary>
        public static List<uint> Hexadecagonals { get { return PolygonNumbers(16); } }

        /// <summary>
        /// Gets Polygon Number for Heptadecagonals
        /// </summary>
        public static List<uint> Heptadecagonals { get { return PolygonNumbers(17); } }

        /// <summary>
        /// Gets Polygon Number for Octadecagonals
        /// </summary>
        public static List<uint> Octadecagonals { get { return PolygonNumbers(18); } }

        /// <summary>
        /// Gets Polygon Number for Nonadecagonals
        /// </summary>
        public static List<uint> Nonadecagonals { get { return PolygonNumbers(19); } }

        /// <summary>
        /// Gets Polygon Number for Icosagonals
        /// </summary>
        public static List<uint> Icosagonals { get { return PolygonNumbers(20); } }

        /// <summary>
        /// Gets Polygon Number for Icosihagonals
        /// </summary>
        public static List<uint> Icosihenagonals { get { return PolygonNumbers(21); } }

        /// <summary>
        /// Gets Polygon Number for Icoidigonals
        /// </summary>
        public static List<uint> Icosidigonals { get { return PolygonNumbers(22); } }

        /// <summary>
        /// Gets Polygon Number for Icoitrigonals
        /// </summary>
        public static List<uint> Icositrigonals { get { return PolygonNumbers(23); } }

        /// <summary>
        /// Gets Polygon Number for Icositetragonals
        /// </summary>
        public static List<uint> Icositetragonals { get { return PolygonNumbers(24); } }

        /// <summary>
        /// Checkes whether specific number is in polygon number series of triangle 
        /// </summary>
        /// <param name="number">Number to search</param>
        /// <returns>Return true if found</returns>
        public static bool IsTriangular(uint number) { return (PolygonNumbers(3).Contains(number)); }

        /// <summary>
        /// Checkes whether specific number is in polygon number series of Square 
        /// </summary>
        /// <param name="number">Number to search</param>
        /// <returns>Return true if found</returns>
        public static bool IsSquare(uint number) { return (PolygonNumbers(4).Contains(number)); }

        /// <summary>
        /// Checkes whether specific number is in polygon number series of Pentagonal 
        /// </summary>
        /// <param name="number">Number to search</param>
        /// <returns>Return true if found</returns>
        public static bool IsPentagonal(uint number) { return (PolygonNumbers(5).Contains(number)); }

        /// <summary>
        /// Checkes whether specific number is in polygon number series of Hexagonal 
        /// </summary>
        /// <param name="number">Number to search</param>
        /// <returns>Return true if found</returns>
        public static bool IsHexagonal(uint number) { return (PolygonNumbers(6).Contains(number)); }

        /// <summary>
        /// Checkes whether specific number is in polygon number series of Heptagonal 
        /// </summary>
        /// <param name="number">Number to search</param>
        /// <returns>Return true if found</returns>
        public static bool IsHeptagonal(uint number) { return (PolygonNumbers(7).Contains(number)); }

        /// <summary>
        /// Checkes whether specific number is in polygon number series of Octagonal 
        /// </summary>
        /// <param name="number">Number to search</param>
        /// <returns>Return true if found</returns>
        public static bool IsOctagonal(uint number) { return (PolygonNumbers(8).Contains(number)); }

        /// <summary>
        /// Checkes whether specific number is in polygon number series of Nonagonal 
        /// </summary>
        /// <param name="number">Number to search</param>
        /// <returns>Return true if found</returns>
        public static bool IsNonagonal(uint number) { return (PolygonNumbers(9).Contains(number)); }

        /// <summary>
        /// Checkes whether specific number is in polygon number series of Decagonal 
        /// </summary>
        /// <param name="number">Number to search</param>
        /// <returns>Return true if found</returns>
        public static bool IsDecagonal(uint number) { return (PolygonNumbers(10).Contains(number)); }

        /// <summary>
        /// Checkes whether specific number is in polygon number series of Hendecagonal 
        /// </summary>
        /// <param name="number">Number to search</param>
        /// <returns>Return true if found</returns>
        public static bool IsHendecagonal(uint number) { return (PolygonNumbers(11).Contains(number)); }

        /// <summary>
        /// Checkes whether specific number is in polygon number series of Dodecagonal 
        /// </summary>
        /// <param name="number">Number to search</param>
        /// <returns>Return true if found</returns>
        public static bool IsDodecagonal(uint number) { return (PolygonNumbers(12).Contains(number)); }

        /// <summary>
        /// Checkes whether specific number is in polygon number series of Tridecagonal 
        /// </summary>
        /// <param name="number">Number to search</param>
        /// <returns>Return true if found</returns>
        public static bool IsTridecagonal(uint number) { return (PolygonNumbers(13).Contains(number)); }

        public static bool IsTetradecagonal(uint number) {
            return (PolygonNumbers(14).Contains(number));
        }

        public static bool IsPentadecagonal(uint number) {
            return (PolygonNumbers(15).Contains(number));
        }

        public static bool IsHexadecagonal(uint number) {
            return (PolygonNumbers(16).Contains(number));
        }

        public static bool IsHeptadecagonal(uint number) {
            return (PolygonNumbers(17).Contains(number));
        }

        public static bool IsOctadecagonal(uint number) {
            return (PolygonNumbers(18).Contains(number));
        }

        public static bool IsNonadecagonal(uint number) {
            return (PolygonNumbers(19).Contains(number));
        }

        public static bool IsIcosagonal(uint number) {
            return (PolygonNumbers(20).Contains(number));
        }

        public static bool IsIcosihenagonal(uint number) {
            return (PolygonNumbers(21).Contains(number));
        }

        public static bool IsIcosidigonal(uint number) {
            return (PolygonNumbers(22).Contains(number));
        }

        public static bool IsIcositrigonal(uint number) {
            return (PolygonNumbers(23).Contains(number));
        }

        public static bool IsIcositetragonal(uint number) {
            return (PolygonNumbers(24).Contains(number));
        }
        #endregion

        #region Centered Ploygon
        //http://en.wikipedia.org/wiki/CenteredPolygonal_number
        // ith number of CenteredPolygon(sides=N) = (((N * i)/2) * (i-1)) + 1
        // Whereas a prime number p cannot be a polygon number, many centered polygon numbers are primes.
        private static Dictionary<uint, List<uint>> centeredPolygonNumbersDictionary = new Dictionary<uint, List<uint>>();

        public static List<uint> CenteredPolygonNumbers(uint sides) {
            if (!centeredPolygonNumbersDictionary.ContainsKey(sides))
                GenerateCenteredPolygonNumbers(sides);

            if (centeredPolygonNumbersDictionary.ContainsKey(sides))
                return centeredPolygonNumbersDictionary[sides];
            else
                return null;
        }

        private static void GenerateCenteredPolygonNumbers(uint sides) {
            List<uint> polygon_numbers = new List<uint>(SeriesLimit);
            for (int n = 1; n <= SeriesLimit; n++) {
                uint number = (uint)(((sides * n) / 2.0) * (n - 1)) + 1;
                polygon_numbers.Add(number);
            }
            centeredPolygonNumbersDictionary.Add(sides, polygon_numbers);
        }

        public static bool IsCenteredPolygonNumber(uint sides, uint number) {
            if (centeredPolygonNumbersDictionary.ContainsKey(sides))
                return (centeredPolygonNumbersDictionary[sides].Contains(number));
            else
                return false;
        }

        public static List<uint> CenteredTriangulars { get { return CenteredPolygonNumbers(3); } }

        public static List<uint> CenteredSquares { get { return CenteredPolygonNumbers(4); } }

        public static List<uint> CenteredPentagonals { get { return CenteredPolygonNumbers(5); } }

        public static List<uint> CenteredHexagonals { get { return CenteredPolygonNumbers(6); } }

        public static List<uint> CenteredHeptagonals { get { return CenteredPolygonNumbers(7); } }

        public static List<uint> CenteredOctagonals { get { return CenteredPolygonNumbers(8); } }

        public static List<uint> CenteredNonagonals { get { return CenteredPolygonNumbers(9); } }

        public static List<uint> CenteredDecagonals { get { return CenteredPolygonNumbers(10); } }

        public static List<uint> CenteredHendecagonals { get { return CenteredPolygonNumbers(11); } }

        public static List<uint> CenteredDodecagonals { get { return CenteredPolygonNumbers(12); } }

        public static List<uint> CenteredTridecagonals { get { return CenteredPolygonNumbers(13); } }

        public static List<uint> CenteredTetradecagonals { get { return CenteredPolygonNumbers(14); } }

        public static List<uint> CenteredPentadecagonals { get { return CenteredPolygonNumbers(15); } }

        public static List<uint> CenteredHexadecagonals { get { return CenteredPolygonNumbers(16); } }

        public static List<uint> CenteredHeptadecagonals { get { return CenteredPolygonNumbers(17); } }

        public static List<uint> CenteredOctadecagonals { get { return CenteredPolygonNumbers(18); } }

        public static List<uint> CenteredNonadecagonals { get { return CenteredPolygonNumbers(19); } }

        public static List<uint> CenteredIcosagonals { get { return CenteredPolygonNumbers(20); } }

        public static List<uint> CenteredIcosihenagonals { get { return CenteredPolygonNumbers(21); } }

        public static List<uint> CenteredIcosidigonals { get { return CenteredPolygonNumbers(22); } }

        public static List<uint> CenteredIcositrigonals { get { return CenteredPolygonNumbers(23); } }

        public static List<uint> CenteredIcositetragonals { get { return CenteredPolygonNumbers(24); } }

        public static bool IsCenteredTriangular(uint number) {
            return (CenteredPolygonNumbers(3).Contains(number));
        }

        public static bool IsCenteredSquare(uint number) {
            return (CenteredPolygonNumbers(4).Contains(number));
        }

        public static bool IsCenteredPentagonal(uint number) {
            return (CenteredPolygonNumbers(5).Contains(number));
        }

        public static bool IsCenteredHexagonal(uint number) {
            return (CenteredPolygonNumbers(6).Contains(number));
        }

        public static bool IsCenteredHeptagonal(uint number) {
            return (CenteredPolygonNumbers(7).Contains(number));
        }

        public static bool IsCenteredOctagonal(uint number) {
            return (CenteredPolygonNumbers(8).Contains(number));
        }

        public static bool IsCenteredNonagonal(uint number) {
            return (CenteredPolygonNumbers(9).Contains(number));
        }

        public static bool IsCenteredDecagonal(uint number) {
            return (CenteredPolygonNumbers(10).Contains(number));
        }

        public static bool IsCenteredHendecagonal(uint number) {
            return (CenteredPolygonNumbers(11).Contains(number));
        }

        public static bool IsCenteredDodecagonal(uint number) {
            return (CenteredPolygonNumbers(12).Contains(number));
        }

        public static bool IsCenteredTridecagonal(uint number) {
            return (CenteredPolygonNumbers(13).Contains(number));
        }

        public static bool IsCenteredTetradecagonal(uint number) {
            return (CenteredPolygonNumbers(14).Contains(number));
        }

        public static bool IsCenteredPentadecagonal(uint number) {
            return (CenteredPolygonNumbers(15).Contains(number));
        }

        public static bool IsCenteredHexadecagonal(uint number) {
            return (CenteredPolygonNumbers(16).Contains(number));
        }

        public static bool IsCenteredHeptadecagonal(uint number) {
            return (CenteredPolygonNumbers(17).Contains(number));
        }

        public static bool IsCenteredOctadecagonal(uint number) {
            return (CenteredPolygonNumbers(18).Contains(number));
        }

        public static bool IsCenteredNonadecagonal(uint number) {
            return (CenteredPolygonNumbers(19).Contains(number));
        }

        public static bool IsCenteredIcosagonal(uint number) {
            return (CenteredPolygonNumbers(20).Contains(number));
        }

        public static bool IsCenteredIcosihenagonal(uint number) {
            return (CenteredPolygonNumbers(21).Contains(number));
        }

        public static bool IsCenteredIcosidigonal(uint number) {
            return (CenteredPolygonNumbers(22).Contains(number));
        }

        public static bool IsCenteredIcositrigonal(uint number) {
            return (CenteredPolygonNumbers(23).Contains(number));
        }

        public static bool IsCenteredIcositetragonal(uint number) {
            return (CenteredPolygonNumbers(24).Contains(number));
        }

        #endregion

        #region Poly Hex
        // https://oeis.org  // Chemical polyhex hydrocarbons with 19 hexagons
        // Number of Isomers of polyhex hydrocarbons with C_(2h) symmetry with nineteen hexagons
        // 3, 17, 66, 189, 589, 1677, 3829, 7948, 15649, 25543, 26931, 15472 
        // Number of isomers of polyhex hydrocarbons with C_(2v) symmetry with nineteen hexagons
        // 3, 17, 14, 92, 60, 316, 175, 814, 495, 2323, 1402, 6037, 3113, 12851, 6200, 24710, 11851, 46152, 18123, 72151, 18007, 74547, 8970, 40141
        private static Dictionary<uint, List<uint>> polyhexNumbersDictionary = new Dictionary<uint, List<uint>>();
        public static List<uint> PolyhexNumbers(uint hexagons) {
            if (!polyhexNumbersDictionary.ContainsKey(hexagons))
                GeneratePolyhexNumbers(hexagons);

            if (polyhexNumbersDictionary.ContainsKey(hexagons))
                return polyhexNumbersDictionary[hexagons];
            else
                return null;
        }

        private static void GeneratePolyhexNumbers(uint hexagons) {
            List<uint> polyhex_numbers = null;
            if (hexagons == 1) {
                polyhex_numbers = new List<uint>() { 3, 17, 66, 189, 589, 1677, 3829, 7948, 15649, 25543, 26931, 15472 };
            } else if (hexagons == 2) {
                polyhex_numbers = new List<uint>() { 3, 17, 14, 92, 60, 316, 175, 814, 495, 2323, 1402, 6037, 3113, 12851, 6200, 24710, 11851, 46152, 18123, 72151, 18007, 74547, 8970, 40141 };
            }

            polyhexNumbersDictionary.Add(hexagons, polyhex_numbers);
        }

        public static bool IsPolyhexNumber(uint hexagons, uint number) {
            if (polyhexNumbersDictionary.ContainsKey(hexagons))
                return (polyhexNumbersDictionary[hexagons].Contains(number));
            else
                return false;
        }
        public static List<uint> C2hPolyhexHydrocarbons { get { return PolyhexNumbers(1); } }

        public static List<uint> C2vPolyhexHydrocarbons { get { return PolyhexNumbers(2); } }

        public static bool IsC2hPolyhexHydrocarbons(uint number) {
            return (PolyhexNumbers(1).Contains(number));
        }

        public static bool IsC2vPolyhexHydrocarbons(uint number) {
            return (PolyhexNumbers(2).Contains(number));
        }

        #endregion

        #region Tetrahedral
        //Tetrahedral,            // n*(n+1)*(n+2)/6   = 1, 4, 10, 20, 35, 56, 84, 120, 165, 220, 286, 364, 455, 560, 680, 816, 969, 1140, 1330, 1540, 1771, 2024, 2300, 2600, 2925, 3276, 3654, 4060, 4495, 4960, 5456, 5984, 6545, 7140, 7770, 8436, 9139, 9880, 10660, 11480, 12341, 13244, 14190, 15180, ...
        private static List<uint> tetrahedrals = null;
        public static List<uint> Tetrahedrals {
            get {
                if (tetrahedrals == null)
                    GenerateTetrahedrals();

                return tetrahedrals;
            }
        }

        private static void GenerateTetrahedrals() {
            tetrahedrals = new List<uint>(SeriesLimit);
            for (uint number = 1; number <= SeriesLimit; number++) {
                uint result = (number * (number + 1) * (number + 2)) / 6;
                tetrahedrals.Add(result);
            }
        }

        public static bool IsTetrahedral(uint number) {
            return (Tetrahedrals.Contains(number));
        }
        #endregion

        #region Pyramid
        //Pyramidal,              // n*(n+1)*(2*n+1)/6 = 1, 5, 14, 30, 55, 91, 140, 204, 285, 385, 506, 650, 819, 1015, 1240, 1496, 1785, 2109, 2470, 2870, 3311, 3795, 4324, 4900, 5525, 6201, 6930, 7714, 8555, 9455, 10416, 11440, 12529, 13685, 14910, 16206, 17575, 19019, 20540, 22140, 23821, 25585, 27434, 29370, ...        
        private static List<uint> pyramidals = null;
        public static List<uint> Pyramidals {
            get {
                if (pyramidals == null)
                    GeneratePyramidals();

                return pyramidals;
            }
        }

        private static void GeneratePyramidals() {
            pyramidals = new List<uint>(SeriesLimit);
            for (uint number = 1; number <= SeriesLimit; number++) {
                uint result = (number * (number + 1) * ((2 * number) + 1)) / 6;
                pyramidals.Add(result);
            }
        }

        public static bool IsPyramidal(uint number) {
            return (Pyramidals.Contains(number));
        }
        #endregion

        #region Cubic
        //Cubic,                  // n^3               = 1, 8, 27, 64, 125, ...        
        private static List<uint> cubics = null;
        public static List<uint> Cubics {
            get {
                if (cubics == null) {
                    GenerateCubics();
                }
                return cubics;
            }
        }
        private static void GenerateCubics() {
            cubics = new List<uint>(SeriesLimit);
            for (uint number = 1; number <= SeriesLimit; number++) {
                cubics.Add(number * number * number);
            }
        }

        public static bool IsCubic(uint number) {
            return (Cubics.Contains(number));
        }
        #endregion

        #region Pivots
        // Pivot numbers for books with pivot/support chapters (eg Quran has N=114, O=N/2=57, O=P1*P2=3*19 has P3=Sum(1..57)=29 verses)        
        private static Dictionary<PivotType, List<long>> pivotsDictionary = null;
        public static List<long> Pivots(PivotType type) {
            if (pivotsDictionary == null)
                GeneratePivots();

            if (pivotsDictionary.ContainsKey(type))
                return pivotsDictionary[type];
            else
                return null;
        }

        private static void GeneratePivots() {
            pivotsDictionary = new Dictionary<PivotType, List<long>>(6);

            // N is the number of chapters in a candidate book (eg 114)
            var pivotNNumbers = new List<long>(SeriesLimit);
            var pivotO_numbers = new List<long>(SeriesLimit);
            var pivotP1_numbers = new List<long>(SeriesLimit);
            var pivotP2_numbers = new List<long>(SeriesLimit);
            var pivotP3_numbers = new List<long>(SeriesLimit);
            var pivotS_numbers = new List<long>(SeriesLimit);

            for (int i = 1; i <= 1000000; i++) {
                var N = 2 + 4 * i;  // 2, 6, 10, 14, 18, 22, ... 114, ...

                // O = pivot chapter = last chapter in the top half (eg 57)
                var O = N / 2;
                if (O.IsOdd()) {
                    var factors = O.Factorize();
                    if (factors.Count == 2)         // O is pseudo-prime
                    {
                        var P1 = factors[0];   // (eg 3)
                        var P2 = factors[1];   // (eg 19)
                        var P3 = (O / 2) + 1;  // (eg 29)

                        if (P3.IsPrime()) {
                            var S = P1 * P3;        // supporting chapter in bottom half (eg 87)
                            if ((S > O) && (S <= N)) {
                                pivotNNumbers.Add(N);
                                pivotO_numbers.Add(O);
                                pivotP1_numbers.Add(P1);
                                pivotP2_numbers.Add(P2);
                                pivotP3_numbers.Add(P3);
                                pivotS_numbers.Add(S);

                                if (pivotNNumbers.Count == SeriesLimit) {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            pivotsDictionary.Add(PivotType.Book, pivotNNumbers);
            pivotsDictionary.Add(PivotType.Chapter, pivotO_numbers);
            pivotsDictionary.Add(PivotType.ChapterFirstFactors, pivotP1_numbers);
            pivotsDictionary.Add(PivotType.ChapterSecondFactors, pivotP2_numbers);
            pivotsDictionary.Add(PivotType.ChapterVerses, pivotP3_numbers);
            pivotsDictionary.Add(PivotType.SupportChapters, pivotS_numbers);
        }

        public static bool IsPivot(PivotType type, uint number) {
            if (pivotsDictionary.ContainsKey(type)) {
                return (pivotsDictionary[type].Contains(number));
            } else {
                return false;
            }
        }
        #endregion
    }
}